package ru.vboxdc.impl.vbox;

import com.sun.xml.ws.commons.virtualbox.*;
import ru.vboxdc.iface.VirtualMachineManager;
import org.apache.log4j.Logger;
import org.virtualbox.*;
import org.virtualbox.SessionState;
import ru.vboxdc.impl.vbox.VBoxConstant;

import javax.xml.ws.WebServiceException;
import java.util.Map;
import java.util.Date;


/**
 * User: lelon
 * Date: 30.01.2009
 * Time: 12:36:55
 */
public class VBoxVirtualMachineManager implements VirtualMachineManager {

    private String OSType;

    private IMachine iMachine;

    private static Integer lastId = 0;

    private static Logger logger = Logger.getLogger(VBoxVirtualMachineManager.class);

    private IVirtualBox virtualBox;

    private IWebsessionManager websessionManager;

    private Map<String, String> parameters;
    //Todo: may by static?
    private long lastTimeSessionCheck;
    /**
     * Attribute <p>
     * State server connection.
     * true - Success  connection
     * false - Failed  connection
     */
    private Boolean stateServerConnection = false;


    public VBoxVirtualMachineManager(IMachine iMachine, Map<String, String> param) {
        logger.debug("Creating new VBoxVirtualMachineManager object for");
        lastId++;
        this.parameters = param;
        this.iMachine = iMachine;
        setDefaultParameters();
        if (this.setConnection()) {

            this.setVRDP(getParameter(VBoxConstant.START_PORT_PARAMETER));
        }
    }

    /**
     * Method set default parameters for connection with server
     */
    private void setDefaultParameters() {
        if ((!parameters.containsKey(VBoxConstant.START_PORT_PARAMETER)) ||
                (parameters.get(VBoxConstant.START_PORT_PARAMETER) == null) &&
                        (parameters.get(VBoxConstant.START_PORT_PARAMETER).equals(""))) {
            parameters.put(VBoxConstant.START_PORT_PARAMETER, VBoxConstant.DEFAULT_START_PORT.toString());
        }
        if ((!parameters.containsKey(VBoxConstant.TIMEOUT_SESSION_STATE_PARAMETER)) ||
                (parameters.get(VBoxConstant.TIMEOUT_SESSION_STATE_PARAMETER) == null) &&
                        (parameters.get(VBoxConstant.TIMEOUT_SESSION_STATE_PARAMETER).equals(""))) {
            parameters.put(VBoxConstant.TIMEOUT_SESSION_STATE_PARAMETER, VBoxConstant.DEFAULT_TIMEOUT_SESSION_STATE.toString());
        }
    }

    public boolean setConnection() {
        websessionManager = new IWebsessionManager(getParameter(VBoxConstant.HOSTNAME_PARAMETER));

        try {
            Date now = new Date();
            virtualBox = websessionManager.logon(getParameter(VBoxConstant.USER_NAME_PARAMETER), getParameter(VBoxConstant.PASSWORD_PARAMETER));
            logger.debug("Initialized connection to VirtualBox version " + virtualBox.getVersion());
            setStateServerConnection(true);
            lastTimeSessionCheck = now.getTime();
            return true;
        } catch (Exception e) {
            logger.error(e);
            setStateServerConnection(false);
            return false;
        }
    }

    public String getParameter(String parameterName) {
        return parameters.get(parameterName);
    }

    public Map<String, String> getParameters() {
        return parameters;
    }

    @Override
    public String getOSType() {
        return OSType;
    }

    @Override
    public void setOSType(String OSType) {
        this.OSType = OSType;
    }

    /**
     * @param startPort Initial VRDP port
     *                  This method enable VRDP server  and set VRDP port
     */
    public void setVRDP(String startPort) {
        Long longStartPort;
        longStartPort = Long.parseLong(startPort);
        ISession session = this.getMutableSession(iMachine);
        IMachine mutable = session.getMachine();
        mutable.getVRDPServer().setEnabled(true);
        mutable.getVRDPServer().setPort(longStartPort + (lastId));
        mutable.saveSettings();
        session.close();
    }

    public ISession getSession() {
        return websessionManager.getSessionObject(virtualBox);
    }

    public ISession getMutableSession(IMachine iMachine) {
        ISession session = websessionManager.getSessionObject(virtualBox);
        switch (iMachine.getSessionState()) {

            case CLOSED:
                virtualBox.openSession(session, iMachine.getId());
                break;

            case CLOSING:
                //TODO: необходимо обработать
                break;

            case NULL:
                break;

            case OPEN:
                virtualBox.openExistingSession(session, iMachine.getId());
                break;

            case SPAWNING:
                break;
        }
        return session;
    }

    public Integer getLastId() {
        return lastId;
    }

    @Override
    public void startVM() throws Exception {
        ISession iSession = this.getSession();
        String nowState=getState();
        if ((nowState.equals("PoweredOff")) ||
                (nowState.equals("Saved")) ||
                (nowState.equals("Aborted")) ||
                (nowState.equals("Paused"))) {
            IProgress iProgress = virtualBox.openRemoteSession(iSession,
                    iMachine.getId(),
                    "vrdp",
                    "");
            logger.debug("Session for VM " + iMachine.getId() + " is opening...");
            iProgress.waitForCompletion(10000);
            long rc = iProgress.getResultCode();
            if (rc != 0) logger.error("Session failed!");
            iSession.close();
        }
    }

    public void stopVM() throws Exception {
        String nowState=getState();
        if ((nowState.equals("Running")) ||
                (nowState.equals("Starting"))) {
            ISession session = this.getMutableSession(iMachine);
            try {
                IConsole console = session.getConsole();
                IProgress iProgress = console.powerDownAsync();
                logger.debug("stopping " + iMachine.getId());
                iProgress.waitForCompletion(10000);

                long rc = iProgress.getResultCode();
                if (rc != 0) logger.error("sessing failed!");

            } finally {
                session.close();
            }
        }

    }

    @Override
    public String getState() {
        if (getWebSessionState().equals("Ok"))
            return iMachine.getState().value();
        else {
            setConnection();
            return iMachine.getState().value();
        }
    }

    public boolean checkTimeoutWebSession() {
        Date nowDate = new Date();
        long nowDateLong=nowDate.getTime();
        int timeout = Integer.parseInt(getParameter(VBoxConstant.TIMEOUT_SESSION_STATE_PARAMETER));
        if (((nowDateLong - lastTimeSessionCheck) / 1000) > timeout)
            return true;
        else
            return false;
    }

    public String getWebSessionState() {
        try {
            if (checkTimeoutWebSession())
                iMachine.getState();
            return "Ok";
        }
        catch (WebServiceException e) {
            return "Error";
        }
    }

    public void setStateServerConnection(Boolean stateServerConnection) {
        this.stateServerConnection = stateServerConnection;
    }

    public Boolean getStateServerConnection() {
        return stateServerConnection;
    }

}
